//@author A0081229H
package storage;

import java.util.ArrayList;

public class Task implements Comparable<Task>, Clonable<Task>, Observer, Observable{

	private static final int INIT_COUNTER = 0;

	private String description;
	private DateRange dateRange;
	private TimeRange timeRange;
	private boolean doneStatus;
	private int copyCounter;
	private boolean overdueStatus;
	private boolean clashingStatus;

	private static final String ERROR_INTEGRITY = "DateTime integrity compromised.";

	private static final String SETDESCRIPTION = "setDescription";
	private static final String SETSTATUS = "setStatus";
	private static final String SETDONE = "setDone";
	private static final String SETPENDING = "setPending";
	private static final String INCREMENTCOPYCOUNTER = "incrementCopyCounter";
	private static final String DESCREMENTCOPYCOUNTER = "decrementCopyCounter";
	private static final String SETCOPYCOUNTER = "setCopyCounter";
	private static final String SETOVERDUE = "setOverdue";
	private static final String SETCLASHINGSTATUS = "setClashingStatus";
	
	private ArrayList<Observer> observerList;

	// Constructors

	public Task(String pDescription, DateRange pDateRange, TimeRange pTimeRange) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		assert (pDescription == null || 
				pDateRange == null || pTimeRange == null): Helper.ERROR_NULL;
		description = pDescription;
		dateRange = pDateRange;
		timeRange = pTimeRange;
		doneStatus = false;
		overdueStatus = false;
		clashingStatus = false;
		copyCounter = INIT_COUNTER;
		observerList = new ArrayList<Observer>();
		dateRange.addObs(this);
		timeRange.addObs(this);
		if (!isDateTimeValid()) {
			throw new Exception(ERROR_INTEGRITY);
		}
	}

	public Task(String pDescription, DateRange pDateRange, TimeRange pTimeRange, boolean pDoneStatus, boolean pOverdueStatus) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		assert (pDescription == null || 
				pDateRange == null || pTimeRange == null): Helper.ERROR_NULL;
		description = pDescription;
		dateRange = pDateRange;
		timeRange = pTimeRange;
		doneStatus = pDoneStatus;
		overdueStatus = pOverdueStatus;
		clashingStatus = false;
		copyCounter = INIT_COUNTER;
		observerList = new ArrayList<Observer>();
		dateRange.addObs(this);
		timeRange.addObs(this);
		if (!isDateTimeValid()) {
			throw new Exception(ERROR_INTEGRITY);
		}
	}


	// Access Methods

	public String getDescription() {
		return description;
	}

	public DateRange getDateRange() {
		return dateRange;
	}

	public TimeRange getTimeRange() {
		return timeRange;
	}

	public boolean getDoneStatus() {
		return doneStatus;
	}

	public boolean getOverdueStatus() {
		return overdueStatus;
	}

	public boolean getClashingStatus() {
		return clashingStatus;
	}

	// Verification Methods

	public boolean isValid() {
		if (isTimed() ||
				isDeadline() ||
				isFloating()) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isTimed() {
		if (dateRange.isComplete() &&
				timeRange.isComplete()) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isDeadline() {
		if (dateRange.isDeadline()) {
			if (timeRange.isDeadline() ||
					timeRange.isEmpty()) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public boolean isFloating() {
		if (timeRange.isEmpty()) {
			if (dateRange.isComplete() ||
					dateRange.isEmpty()) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public boolean isDone() {
		return doneStatus;
	}

	public int getCopyCounter() {
		return copyCounter;
	}

	public boolean isOverdue() {
		return overdueStatus;
	}

	public boolean isClashing() {
		return clashingStatus;
	}

	public boolean isDateTimeValid() {
		if (isTimed()) {
			Date startDate = dateRange.getStartDate();
			Date endDate = dateRange.getEndDate();
			Time startTime = timeRange.getStartTime();
			Time endTime = timeRange.getEndTime();
			if (startDate.compareTo(endDate) == Helper.SAME &&
					endTime.compareTo(startTime) == Helper.LESS) {
				return false;
			} 
		}
		return true;
	}


	// Modification Methods

	public void setDescription(String pDescription) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETDESCRIPTION + Helper.SPACE +
					pDescription);
		} catch (Exception e) {

		}
		description = pDescription;
		notifyObs();
	}

	public void setStatus(boolean pDoneStatus) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETSTATUS + Helper.SPACE +
					pDoneStatus);
		} catch (Exception e) {

		}
		doneStatus = pDoneStatus;
	}

	public void setDone() {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETDONE);
		} catch (Exception e) {

		}
		doneStatus = true;
		notifyObs();
	}

	public void setPending() {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETPENDING);
		} catch (Exception e) {

		}
		doneStatus = false;
		notifyObs();
	}

	public void incrementCopyCounter() {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + INCREMENTCOPYCOUNTER);
		} catch (Exception e) {

		}
		copyCounter++;
		notifyObs();
	}

	public void decrementCopyCounter() {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + DESCREMENTCOPYCOUNTER);
		} catch (Exception e) {

		}
		copyCounter--;
		notifyObs();
	}

	public void setCopyCounter(int i) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETCOPYCOUNTER + Helper.SPACE +
					i);
		} catch (Exception e) {

		}
		copyCounter = i;
	}

	public void setOverdue() {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETOVERDUE);
		} catch (Exception e) {

		}
		overdueStatus = true;
	}

	public void setClashingStatus(boolean pClashingStatus) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETCLASHINGSTATUS + Helper.SPACE +
					pClashingStatus);
		} catch (Exception e) {

		}
		clashingStatus = pClashingStatus;
	}

	// Interfaces

	@Override
	public int compareTo(Task pTask) {
		return Helper.compareSameStyle(getDateRange().compareTo(pTask.getDateRange()),
				Helper.compareSameStyle(getTimeRange().compareTo(pTask.getTimeRange()),
						compareSub(pTask)));

	}

	// Helper

	private int compareSub(Task pTask) {
		if (getDoneStatus() == pTask.getDoneStatus()) {
			return getDescription().compareTo(pTask.getDescription());
		} else {
			if (getDoneStatus() && !pTask.getDoneStatus()) {
				return Helper.MORE;
			} else {
				return Helper.LESS;
			}
		}
	}

	@Override
	public Task cloneSelf() throws Exception {
		Task clone;
		String cloneDesc = new String(getDescription());
		DateRange cloneDateRange = getDateRange().cloneSelf();
		TimeRange cloneTimeRange = getTimeRange().cloneSelf();
		boolean cloneDoneStatus = getDoneStatus();
		boolean cloneOverDueStatus = getOverdueStatus();
		clone = new Task(cloneDesc, cloneDateRange, cloneTimeRange, cloneDoneStatus, cloneOverDueStatus);
		return clone;
	}

	@Override
	public void update() {
		notifyObs();
		if (!isTimed()) {
			clashingStatus = false;
		}
	}

	@Override
	public void notifyObs() {
		for (Observer o : observerList) {
			System.out.println(o.toString());
		}

		for (Observer o : observerList) {
			o.update();
		}
	}

	@Override
	public void addObs(Observer o) {
		observerList.add(o);

	}
}
